package com.example.videopractice.mediaex

import android.media.*
import android.os.Environment
import com.example.base.util.ALog
import kotlinx.coroutines.Dispatchers.IO
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import java.io.File

class H264AudioEncoder(
    private val sampleRateInHz: Int,
    private val channelConfig: Int,
    private val audioFormat: Int
) {

    private val recordBufSize: Int =
        AudioRecord.getMinBufferSize(sampleRateInHz, channelConfig, audioFormat)
    private var audioRecord = AudioRecord(
        MediaRecorder.AudioSource.MIC,
        sampleRateInHz,
        channelConfig,
        audioFormat,
        recordBufSize
    )
    private var mediaCodec: MediaCodec =
        MediaCodec.createEncoderByType(MediaFormat.MIMETYPE_AUDIO_AAC)
    private val path =
        Environment.getExternalStorageDirectory().absolutePath + File.separator + "temp_audio.mp4"
    private val mediaMuxer = MediaMuxer(path, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4)
    private var audioTrack = -1

    private var isRunning = false

    /***
     * 开始编码
     */
    fun startEncoder() {
        isRunning = true
        GlobalScope.launch(IO) {
            val audioFormat =
                MediaFormat.createAudioFormat(MediaFormat.MIMETYPE_AUDIO_AAC, sampleRateInHz, 1)
            audioFormat.setInteger(
                MediaFormat.KEY_AAC_PROFILE,
                MediaCodecInfo.CodecProfileLevel.AACObjectLC
            )
            audioFormat.setInteger(MediaFormat.KEY_BIT_RATE, 64 * 1000)
            audioFormat.setInteger(MediaFormat.KEY_CHANNEL_COUNT, 1)
            audioFormat.setInteger(MediaFormat.KEY_SAMPLE_RATE, sampleRateInHz)
            mediaCodec.configure(audioFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE)
            mediaCodec.start()

            audioRecord.startRecording()
            val buffer = ByteArray(recordBufSize)
            while (isRunning) {
                val readBytes = audioRecord.read(buffer, 0, recordBufSize)
                try {
                    encode(buffer, readBytes)
                } catch (e: java.lang.Exception) {
                    ALog.e("xiao", "解码音频(Audio)数据 失败")
                    e.printStackTrace()
                }
            }

            try {
                audioRecord.stop()
                audioRecord.release()
            } catch (e: Exception) {
                ALog.e("xiao", e.message)
                e.printStackTrace()
            }

            try {
                mediaMuxer.stop()
                mediaMuxer.release()
            } catch (e: Exception) {
                ALog.e("xiao", e.message)
                e.printStackTrace()
            }

            try {
                mediaCodec.stop()
                mediaCodec.release()
            } catch (e: Exception) {
                ALog.e("xiao", e.message)
                e.printStackTrace()
            }

            ALog.e("xiao", "结束了")
        }
    }

    private val bufferInfo = MediaCodec.BufferInfo()
    private fun encode(byteArray: ByteArray, readBytes: Int) {
        val inputBufferIndex = mediaCodec.dequeueInputBuffer(TIMEOUT_USEC) //间隔多久 0为立即返回
        if (inputBufferIndex < 0) return
        val inputBuffer = mediaCodec.getInputBuffer(inputBufferIndex)
        if (inputBuffer != null) {
            inputBuffer.put(byteArray)
            if (readBytes <= 0) {
                ALog.e("xiao", "send BUFFER_FLAG_END_OF_STREAM")
                mediaCodec.queueInputBuffer(
                    inputBufferIndex,
                    0,
                    0,
                    System.nanoTime() / 1000,
                    MediaCodec.BUFFER_FLAG_END_OF_STREAM
                )
            } else {
                mediaCodec.queueInputBuffer(
                    inputBufferIndex,
                    0,
                    readBytes,
                    System.nanoTime() / 1000,
                    0
                )
            }
        }

        /*获取解码后的数据*/
        var outputBufferIndex = mediaCodec.dequeueOutputBuffer(bufferInfo, TIMEOUT_USEC)

        if (outputBufferIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
            audioTrack = mediaMuxer.addTrack(mediaCodec.outputFormat)
            ALog.e("xiao", "format改变, audioTrack: $audioTrack")
            if (audioTrack >= 0) {
                mediaMuxer.start()
                ALog.e("xiao", "开始混合")
            }
        }
        while (outputBufferIndex >= 0) {
            if (audioTrack >= 0) {
                val outBuffer = mediaCodec.getOutputBuffer(outputBufferIndex)
                if (bufferInfo.flags and MediaCodec.BUFFER_FLAG_CODEC_CONFIG != 0) {
                    bufferInfo.size = 0
                }
                if (bufferInfo.size != 0 && outBuffer != null) {
                    mediaMuxer.writeSampleData(audioTrack, outBuffer, bufferInfo)
                }
                mediaCodec.releaseOutputBuffer(outputBufferIndex, false)
            }
            outputBufferIndex = mediaCodec.dequeueOutputBuffer(bufferInfo, TIMEOUT_USEC)
        }
    }

    /**
     * 停止编码数据
     */
    fun stopEncoder() {
        isRunning = false
    }

    /**
     * 根据帧数生成时间戳
     */
    private val frameRate = 30
    private fun computePresentationTime(frameIndex: Long): Long {
        return 132 + frameIndex * 1000000 / frameRate
    }

    companion object {
        private const val TIMEOUT_USEC = 12000L
    }
}